﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Collections.ObjectModel;
using ReactiveCode.Util;

namespace ReactiveCode.Linq
{
    public static partial class ObservableEnumerable
    {
        #region Create

        class ObservableEnumerableImpl<T> : IObservableEnumerable<T>
        {
            internal Func<IEnumerableObserver<T>, IDisposable> _subscribe;
            public IDisposable Subscribe(IEnumerableObserver<T> observer)
            {
                return _subscribe(observer);
            }
        }
        public static IObservableEnumerable<T> CreateWithDisposable<T>(Func<IEnumerableObserver<T>, IDisposable> subscribe)
        {
            Contract.Requires(subscribe != null);
            return new ObservableEnumerableImpl<T> { _subscribe = subscribe };
        }
        public static IObservableEnumerable<T> Create<T>(Func<IEnumerableObserver<T>, Action> subscribe)
        {
            Contract.Requires(subscribe != null);
            return CreateWithDisposable<T>(o => Disposable.Create(subscribe(o)));
        }

        #endregion

        #region Subscribe
        public static IDisposable Subscribe<T>(this IObservableEnumerable<T> source, Action<IObservable<T>, int> onAdded, Action<IObservable<T>, int> onRemoved, Action<IEnumerable<IObservable<T>>> onReset,
            Action<Exception> onError, Action onCompleted = null)
        {
            Contract.Requires(source != null && onAdded != null && onRemoved != null && onReset != null && onError != null);
            return source.Subscribe(EnumerableObserver.Create(onAdded, onRemoved, onReset, onError, onCompleted));
        }
        public static IDisposable Subscribe<T>(this IObservableEnumerable<T> source, Action<IObservable<T>, int> onAdded, Action<IObservable<T>, int> onRemoved, Action<IEnumerable<IObservable<T>>> onReset)
        {
            return source.Subscribe(onAdded, onRemoved, onReset, ex => { throw ex; }, null);
        }
        public static IDisposable SubscribeWithClear<T>(this IObservableEnumerable<T> source, Action<IObservable<T>, int> onAdded, Action<IObservable<T>, int> onRemoved, Action onCleared)
        {
            Contract.Requires(onCleared != null);
            return source.Subscribe(onAdded, onRemoved,
                next =>
                {
                    onCleared();
                    int i = 0;
                    foreach (var item in next)
                        onAdded(item, i++);
                });
        }
        #endregion

        internal static LatestEnumerable<T> LatestContinuous<T>(this IObservableEnumerable<T> source)
        {
            Contract.Requires(source != null);
            return new LatestEnumerable<T>(source);
        }
    }
}
